﻿using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

namespace GameFlowManager.Menus
{
	public class Menu : List<MenuItem>
	{
		#region Constants

		private const string ASSET_NAMEFONT = "Fonts/MenuNameFont";
		private const string ASSET_DESCRIPTIONFONT = "Fonts/MenuDescriptionFont";
		private const string ASSET_SELECTOR = "Textures/Selector";

		#endregion

		#region Events

		public Action<Menu> Closed;

		#endregion

		#region Variables

		private SpriteFont _nameFont;
		private SpriteFont _descriptionFont;
		private Texture2D _selector;

		private float _weightedRadius;
		private float _radiusWeight;

		private float _rotation;
		private float _destinationRotation;
		private float _rotationWeight;

		private int _selectedIndex;
		private bool _isClosing;

		private KeyboardState _previousKeyboardState;

		private Game _game;

		#endregion

		#region Constructors

		public Menu(Game game)
			: base()
		{
			_game = game;

			var viewport = _game.GraphicsDevice.Viewport;

			Position = new Vector2(viewport.Width, viewport.Height) / 2.0f;
			_weightedRadius = 0.0f;
			Radius = 100.0f;
			_radiusWeight = 0.0f;
			_selectedIndex = 0;

			_previousKeyboardState = Keyboard.GetState();
			_isClosing = false;
		}

		#endregion

		#region Properties

		public Vector2 Position { get; set; }

		public float Radius { get; set; }

		public bool IsClosed
		{
			get
			{
				return _isClosing && (_radiusWeight <= 0.0f);
			}
		}

		#endregion

		#region Methods

		public void Close()
		{
			_isClosing = true;
			_radiusWeight = 1.0f;
			_weightedRadius = Radius;
		}

		public void LoadContent()
		{
			_nameFont = _game.Content.Load<SpriteFont>(ASSET_NAMEFONT);
			_descriptionFont = _game.Content.Load<SpriteFont>(ASSET_DESCRIPTIONFONT);
			_selector = _game.Content.Load<Texture2D>(ASSET_SELECTOR);
		}

		public void Update(GameTime gameTime)
		{
			if (_isClosing)
			{
				_radiusWeight -= 0.01f;
				_weightedRadius = MathHelper.SmoothStep(0.0f, _weightedRadius, _radiusWeight);

				if ((_radiusWeight <= 0.0f) && (Closed != null))
				{
					Closed(this);
				}
			}
			else if (_radiusWeight < 1.0f)
			{
				_radiusWeight += 0.01f;
				_weightedRadius = MathHelper.SmoothStep(_weightedRadius, Radius, _radiusWeight);
			}

			if (!_isClosing)
			{
				if (_destinationRotation == _rotation)
				{
					var keys = Keyboard.GetState();
					if (keys.IsKeyDown(Keys.Left))
					{
						_destinationRotation = _rotation - MathHelper.TwoPi / Count;
						_rotationWeight = 0.0f;

						_selectedIndex++;
						if (_selectedIndex >= Count)
						{
							_selectedIndex = 0;
						}
					}
					if (keys.IsKeyDown(Keys.Right))
					{
						_destinationRotation = _rotation + MathHelper.TwoPi / Count;
						_rotationWeight = 0.0f;

						_selectedIndex--;
						if (_selectedIndex < 0)
						{
							_selectedIndex = Count - 1;
						}
					}
					if (keys.IsKeyDown(Keys.Enter) && _previousKeyboardState.IsKeyUp(Keys.Enter))
					{
						if (this[_selectedIndex].Action != null)
						{
							this[_selectedIndex].Action(this);
						}
					}

					_previousKeyboardState = keys;
				}
				else
				{
					_rotationWeight += 0.05f;
					_rotation = MathHelper.SmoothStep(_rotation, _destinationRotation, _rotationWeight);
				}
			}
		}

		public void Draw(GameTime gameTime, SpriteBatch spriteBatch)
		{
			var color = new Color(1.0f, 1.0f, 1.0f, _radiusWeight * 2.0f);
			var shadow = new Color(0.0f, 0.0f, 0.0f, _radiusWeight * 2.0f);

			if (!_isClosing)
			{
				spriteBatch.Draw(_selector, Position + Vector2.UnitX * _weightedRadius + Vector2.One, null, shadow, 0.0f, new Vector2(40, 40), 1.0f, SpriteEffects.None, 0.0f);
				spriteBatch.Draw(_selector, Position + Vector2.UnitX * _weightedRadius, null, color, 0.0f, new Vector2(40, 40), 1.0f, SpriteEffects.None, 0.0f);
			}

			var rotation = _rotation;
			for (int index = 0; index < Count; index++)
			{
				var position = Position + new Vector2(_weightedRadius * (float)Math.Cos(rotation), _weightedRadius * (float)Math.Sin(rotation));
				var origin = new Vector2(this[index].Icon.Width, this[index].Icon.Height) / 2.0f;
				var scale = 1.0f;

				spriteBatch.Draw(this[index].Icon, position + Vector2.One, null, shadow, 0.0f, origin, scale, SpriteEffects.None, 0.0f);
				spriteBatch.Draw(this[index].Icon, position, null, color, 0.0f, origin, scale, SpriteEffects.None, 0.0f);

				spriteBatch.DrawString(_nameFont, this[index].Name, position + Vector2.UnitY * 32 - Vector2.UnitX * _nameFont.MeasureString(this[index].Name).X / 2.0f + Vector2.One, shadow);
				spriteBatch.DrawString(_nameFont, this[index].Name, position + Vector2.UnitY * 32 - Vector2.UnitX * _nameFont.MeasureString(this[index].Name).X / 2.0f, color);

				rotation += MathHelper.TwoPi / Count;
			}

			if (!_isClosing)
			{
				spriteBatch.DrawString(_descriptionFont, this[_selectedIndex].Description, Position + new Vector2(_weightedRadius * (float)Math.Cos(MathHelper.PiOver2), (_weightedRadius + _descriptionFont.LineSpacing) * (float)Math.Sin(MathHelper.PiOver2)) - Vector2.UnitX * _descriptionFont.MeasureString(this[_selectedIndex].Description).X / 2.0f + Vector2.One, shadow);
				spriteBatch.DrawString(_descriptionFont, this[_selectedIndex].Description, Position + new Vector2(_weightedRadius * (float)Math.Cos(MathHelper.PiOver2), (_weightedRadius + _descriptionFont.LineSpacing) * (float)Math.Sin(MathHelper.PiOver2)) - Vector2.UnitX * _descriptionFont.MeasureString(this[_selectedIndex].Description).X / 2.0f, color);
			}
		}

		#endregion
	}
}